/* gdt.c 
 * This file is modified form Bram's Kernel Development Tutorial
 * set the new gdt, the new gdt table has 256 entrys
 */
#include <types.h>
#include <pm.h>
#include <string.h>
#include <printk.h>

struct tss_desc tss;
struct seg_desc gdtTable[NR_GDT];
struct desc_ptr  gdtPtr;
selector_t ldt_sel = seg_to_sel(SEG_LDT_TASK);

/**
 * @brief gdt_install
 * 安装一个 段描述符到 GDT
 * @param num 
 * @param base 
 * @param limit 
 * @param attr 
 */
STATIC_INLINE void gdt_install(uint8_t num, uint32_t base, 
                                uint32_t limit, uint32_t attr)
{
    gdtTable[num].base_low = (base & 0xffff);
    gdtTable[num].base_middle = (base >> 16) & 0xff;
    gdtTable[num].base_high = (base >> 24) & 0xff;

    gdtTable[num].limit_low = (limit & 0xffff);
    gdtTable[num].limit_high = ((limit >> 16) & 0x0f);

    gdtTable[num].flags = attr >> 24;
    gdtTable[num].access = attr & 0xff;
}
/**
 * @brief arch_tss_init
 * tss 初始化 
 */
STATIC_INLINE void arch_tss_init(){
    tss.ss0 = seg_to_sel(SEG_DATA_SYS);
    tss.iopb_off = sizeof(tss);
}
/**
 * @brief load_gdt
 * 将 bootloader 的 GDT 复制到 gdtTable
 */
STATIC_INLINE void load_gdt()
{
    memcpy(
        gdtTable,
        (void*)gdtPtr.base,
        (uint32_t)(gdtPtr.limit + 1)
    );
}
/**
 * @brief gdt_init
 * 初始化 GDT 中的表项，如果有增加
 * 请添加在这里
 */
STATIC_INLINE void gdt_init()
{
    gdt_install(SEG_TSS, (uint32_t)&tss, sizeof(tss) - 1, DA_386TSS);
    gdt_install(SEG_LDT_TASK, 0, sizeof(struct seg_desc) * 2, DA_LDT | DA_32);
}
/**
 * @brief arch_gdt_init
 * 初始化 gdt
 */
void arch_gdt_init()
{
    load_gdt();
    gdt_init();
    gdtPtr.limit = (sizeof(struct seg_desc) * NR_GDT) - 1;
    gdtPtr.base = (uint32_t)&gdtTable[0];
    arch_tss_init();
}
/**
 * @brief Set the task ldt object
 * 每一次恢复进程现场，需要先设置它的 ldt 的基地址
 * @param base 
 * @param dpl 
 */
void set_task_ldt(uint32_t base, uint8_t dpl)
{
    gdtTable[SEG_LDT_TASK].base_low = (base & 0xffff);
    gdtTable[SEG_LDT_TASK].base_middle = (base >> 16) & 0xff;
    gdtTable[SEG_LDT_TASK].base_high = (base >> 24) & 0xff;
    gdtTable[SEG_LDT_TASK].access &= DPL_MASK;
    gdtTable[SEG_LDT_TASK].access |= dpl;
}
/**
 * @briefgdt_base
 * 获取 gdtTable 的基地址
 */
struct seg_desc* gdt_base(void)
{
    return gdtTable;
}